import sys, os, re, types, time, glob, traceback
import zipfile
import pickle
from xml.dom import minidom

class Version:
    "Holds a supported version number"
    def __init__(self):
        self.versionString = ""
        self.major         = 0
        self.minor         = 0
        self.bugfix        = 0
        self.printString   = "No Version Information"
    
    def setVersion(self,vstring):
        versionTuple = re.compile(r'''
              # ignore beginning bullshit
        (\d+) # any number of digits
        \.    # a single '.'
        (\d+) # another set of digits
        \.*   # another optional '.'
        (\d*) # a final set of digits, or not
        .*    # anything can come after it as well
        ''', re.VERBOSE)
        if versionTuple.search(vstring):
            self.versionString = vstring
            (self.major,self.minor,self.bugfix) = versionTuple.search(vstring).groups()
            self.printString = ".".join(v for v in [self.major,self.minor,self.bugfix]
                                            if v is not '')
        else:
            self.printString = "UVI: " + vstring #"Unsupported Version Information"
            return False
    
    def __cmp__(self,cmpVersion):
        if isinstance( cmpVersion, Version ):
            if cmp(self.major,cmpVersion.major) is not 0:
                return cmp(self.major,cmpVersion.major)
            elif cmp(self.minor,cmpVersion.minor) is not 0:
                return cmp(self.minor,cmpVersion.minor)
            else:
                return cmp(self.bugfix,cmpVersion.bugfix)
        else:
            # compare them as numbers ( 1.3.5 => 100300500 > cmpVersion )
            return cmp("%03d%03d%03d" % (self.major,self.minor,self.bugfix),cmpVersion)

class AddOn:
    "A single AddOn"
    def __init__(self):
        self.File          = ""
        self.Notes         = ""
        self.Author        = ""
        self.Title         = ""
        self.Interface     = ""
        self.Version       = Version()
        self.VersionString = ""
        self.Package       = ""
        self.Dependencies  = []
        self.OptionalDeps  = []
        self.IndexTime     = 0
        self.Siblings      = []
        self.TOCString     = ""
       
    def __cmp__(self, addon):
        if isinstance( addon, AddOn ):
            if self.Interface != addon.Interface:
                return cmp(self.Interface,addon.Interface)
            elif self.Version and addon.Version:
                return cmp(self.Version, addon.Version)
        else:
            if self.Interface != addon:
                return cmp(self.Interface,addon)
            elif self.Version:
                return cmp(self.Version, addon)
      
    def alphaCmp(self, addon):
        if isinstance(addon, AddOn):
            return cmp(self.Package or self.Title,addon.Package or addon.Title)
        else:
            return cmp(self.Package or self.Title,addon)        

    def __str__(self):
        retStr = "%s\n" % self.Title
        retStr += "Addon    : %s\n" % self.Package
        retStr += "Interface: %s\n" % self.Interface
        retStr += "Version  : %s\n" % self.VersionString
        retStr += "Notes    : %s\n" % self.Notes
        retStr += "Author   : %s" % self.Author # leave the last \n off
        return retStr
    
    def readXML(self,xml):
        for child in xml.childNodes:
            if child.nodeType == child.ELEMENT_NODE:
                if child.tagName == "File":
                    try:
                        self.File = child.firstChild.data.strip()
                    except AttributeError:
                        self.File = ""
                elif child.tagName == "Notes":
                    try:
                        self.Notes = child.firstChild.data.strip()
                    except AttributeError:
                        self.Notes = ""
                elif child.tagName == "Author":
                    try:
                        self.Author = child.firstChild.data.strip()
                    except AttributeError:
                        self.Author = ""
                elif child.tagName == "Title":
                    try:
                        self.Title = child.firstChild.data.strip()
                    except AttributeError:
                        self.Title = ""
                elif child.tagName == "Interface":
                    try:
                        self.Interface = child.firstChild.data.strip()
                    except AttributeError:
                        self.Interface = 0
                elif child.tagName == "Version":
                    try:
                        self.VersionString = child.firstChild.data.strip()
                        self.Version.setVersion(self.VersionString)
                    except AttributeError:
                        self.VersionString = "0.0.0"
                        self.Version.setVersion(self.VersionString)
                elif child.tagName == "Package":
                    try:
                        self.Package = child.firstChild.data.strip()
                    except AttributeError:
                        self.Package = ""
                elif child.tagName == "Dependencies":
                    for item in child.childNodes:
                        if item.nodeType == item.ELEMENT_NODE:
                            try:
                                self.Dependencies.append(item.firstChild.data.strip())
                            except AttributeError:
                                pass
                elif child.tagName == "OptionalDeps":
                    for item in child.childNodes:
                        if item.nodeType == item.ELEMENT_NODE:
                            try:
                                self.OptionalDeps.append(item.firstChild.data.strip())
                            except AttributeError:
                                pass
    def readMeta(self,meta):
      try:
        (self.Package,self.IndexTime,self.Siblings) = pickle.loads(meta)
      except:
        print 'readMeta failed for ', meta
    
    def readTOC(self,toc):
        # this matches ## attribute: value
        self.TOCString = toc
        tocPattern = re.compile(r'## (.*): (.*)')
        lines = toc.split('\n')
        for line in lines:
            pieces = tocPattern.search(line)
            if pieces is not None:
                pieces = pieces.groups()
                if pieces[0] == 'Notes':
                    self.Notes = pieces[1].strip()
                elif pieces[0] == 'Author':
                    self.Author = pieces[1].strip()
                elif pieces[0] == 'Title':
                    self.Title = pieces[1].strip()
                elif pieces[0] == 'Interface':
                    self.Interface = pieces[1].strip()
                elif pieces[0] == 'Version':
                    self.VersionString = pieces[1].strip()
                    self.Version.setVersion(self.VersionString)
                elif pieces[0] in ('Dependencies','RequiredDeps','Dependancies'):
                    self.Dependencies = [f.strip() for f in pieces[1].split(',')]
                elif pieces[0] == 'OptionalDeps':
                    self.OptionalDeps = [f.strip() for f in pieces[1].split(',')]

class wgZip:
  "A zipfile wrapper"
  def __init__(self,archive,mode):
    try:
      self.archive = zipfile.ZipFile(archive,mode)
    except:
      print sys.exc_info()
      sys.exit()

  def rootFolderNames(self):
    "List the root folders in the zipfile"
    ret = []
    for member in self.archive.namelist():
      if member.count('/') is 1 and member[-1] is '/':
        ret.append(member)
    return ret

  def rootNames(self):
    "List root files"
    ret = []
    for member in self.archive.namelist():
      if member.count('/') is 0 or ( member.count('/') is 1 and member[-1] is '/' ):
        ret.append(member)
    return ret

  def subFolders(self,parent):
    "Lists subfolders under a member"
    ret = []
    for member in self.archive.namelist():
      if member is not parent and member.find(parent) is not -1 and member[-1] is '/':
        ret.append(member)
    return ret

  def children(self,parent):
    "returns ZipInfo instances of all the children of the parent"
    if parent[-1] is not '/':
      parent += '/'
    if parent not in self.folderNames():
      return None
    else:
      ret = []
      for member in self.archive.namelist():
        if member.find(parent) is not -1:
          ret.append(self.archive.getinfo(member))
      return ret
    
  def folderNames(self):
    "Return a list of folders"
    ret = []
    for member in self.archive.namelist():
      if member[-1] is '/':
        ret.append(member)
    return ret

  def getToc(self,parent=None):
    "returns text of toc files"
    ret = {}
    if parent is None:
      for member in self.archive.namelist():
        if member.find('.toc') is not -1:
          ret[member] = self.archive.read(member)
    else:
      if parent[-1] is not '/':
        parent += '/'
      if parent in self.rootFolderNames():
        myRegExp = re.compile("^" + parent + ".*\.toc$")
        for member in self.archive.namelist():
          if myRegExp.match(member):
            ret[member] = self.archive.read(member)
    return ret

  def spawn(self,parent,filename):
    "Create a new zipfile consisting of parent and it's children"
    if parent[-1] is not '/':      
      parent += '/'
    if parent not in self.folderNames():
      return None
    else:
      try:
        ret = zipfile.ZipFile(filename,'w')
        # Write the Children
        for child in self.children(parent):
          ret.writestr(child,self.archive.read(child.filename))
        ret.close()
      except:
        print sys.exc_info()
        return None

  def isProcessed(self):
    "Looks for a processed.dat file in the root of the archive"
    return 'processed.dat' in self.archive.namelist()

  def markProcessed(self):
    try:
      if self.isProcessed() == False:
        self.archive.writestr('processed.dat','1')
      return True
    except:
      traceback.print_exc(file=sys.stdout)
      return False
  
  def writestr(self,path_or_info,bytes):
    try:
      self.archive.writestr(path_or_info,bytes)
    except:
      print 'Could not pass to writestr', path_or_info
      print sys.exc_info()
      
  def close(self):
    "Interface to zipfile.close()"
    try:
      self.archive.close()
    except zipfile.error:
      print sys.exc_info()

if __name__ is '__main__':
  pass
